using System;
using System.Collections.Generic;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using Weazel.Gezel.Model;
using Weazel.Gezel.Conversions;
using Weazel.Gezel.Conversions.Model;

using GezelModel = Weazel.Gezel.Model.Model;

namespace Weazel.Gezel.ConversionsTest
{
  [TestClass]
  public class ControllerConverterTest
  {
    /// <summary>
    /// Tests the list of signal flow graph execution
    /// sets returned for a datapath controller by
    /// a hardwired controller
    /// </summary>
    [TestMethod]
    public void TestHardwiredSfgExecutionSet()
    {
      GezelModel model = GezelModelHelper.BuildModel("dp mydp { sfg exec { } } hardwired myctrl (mydp) { exec; }");
      Controller gezelController = model.Controllers["myctrl"];
   
      List<ExecutionSet> result = ControllerConversionHelper.getControlSteps(new SoftwareModel(model), gezelController);
      Assert.AreEqual(result.Count, 1);

      ExecutionSet sfgExecutionSet = result[0];
      Assert.AreEqual(sfgExecutionSet.Count, 1);
      
      // should be true since sfg parent datapath is controlled by hardwired
      Assert.AreEqual(sfgExecutionSet[0].IsHardwired, true); 
    }

    /// <summary>
    /// Tests the list of signal flow graph execution
    /// sets returned for a datapath controller by
    /// a sequencer controller
    /// </summary>
    [TestMethod]
    public void TestSequencerSfgExecutionSet()
    {
      GezelModel model = GezelModelHelper.BuildModel(
        "dp mydp { sfg exec1 { } sfg exec2 { } sfg dummy {} } sequencer myctrl (mydp) { exec1; exec2; }"
      );
      Controller gezelController = model.Controllers["myctrl"];

			List<ExecutionSet> result = ControllerConversionHelper.getControlSteps(new SoftwareModel(model), gezelController);
      Assert.AreEqual(result.Count, 2); // last sfg should not be included, remember

      foreach (ExecutionSet sfgExecutionSet in result)
      {
        Assert.AreEqual(sfgExecutionSet.Count, 1);
        Assert.IsTrue(sfgExecutionSet[0].LocalName.StartsWith("exec"));
        // should be false since sfg parent datapath is controlled by sequencer
        Assert.AreEqual(sfgExecutionSet[0].IsHardwired, false);
      }
    }

    /// <summary>
    /// Tests the list of signal flow graph execution
    /// sets returned for a datapath controller by
    /// a simple unconditional fsm controller
    /// </summary>
    [TestMethod]
    public void TestSimpleUnconditionalFsmSfgExecutionSet()
    {
      GezelModel model = GezelModelHelper.BuildModel(
        @"dp mydp { sfg exec1 { } sfg exec2 { } sfg dummy {} } 
          
          fsm myctrl (mydp) { 
            initial s1;
            state s2;
    
            @s1 (exec1, exec2) -> s2;          
            @s2 (exec2, exec1) -> s1;
          }"
      );
      Controller gezelController = model.Controllers["myctrl"];

			List<ExecutionSet> result = ControllerConversionHelper.getControlSteps(new SoftwareModel(model), gezelController);
      Assert.AreEqual(result.Count, 2); 

      ExecutionSet sfgExecutionSet;

      // check first sfg execution set
      sfgExecutionSet = result[0];
      Assert.AreEqual(sfgExecutionSet.Count, 2);
      Assert.AreEqual(sfgExecutionSet[0].LocalName, "exec1");
      Assert.AreEqual(sfgExecutionSet[1].LocalName, "exec2");
      // should be false since sfg parent datapath is controlled by fsm
      Assert.AreEqual(sfgExecutionSet[0].IsHardwired, false);
      Assert.AreEqual(sfgExecutionSet[1].IsHardwired, false);

      // check second sfg execution set
      sfgExecutionSet = result[1];
      Assert.AreEqual(sfgExecutionSet.Count, 2);
      Assert.AreEqual(sfgExecutionSet[0].LocalName, "exec2");
      Assert.AreEqual(sfgExecutionSet[1].LocalName, "exec1");
      // should be false since sfg parent datapath is controlled by fsm
      Assert.AreEqual(sfgExecutionSet[0].IsHardwired, false);
      Assert.AreEqual(sfgExecutionSet[1].IsHardwired, false);
    }

    /// <summary>
    /// Tests the list of signal flow graph execution
    /// sets returned for a datapath controller by
    /// a simple conditional fsm controller
    /// </summary>
    [TestMethod]
    public void TestSimpleConditionalFsmSfgExecutionSet()
    {
      GezelModel model = GezelModelHelper.BuildModel(
        @"dp mydp { reg rx : ns(1); sfg exec1 { } sfg exec2 { } sfg dummy {} } 
          
          fsm myctrl (mydp) { 
            initial s1;
            state s2;
    
            @s1 if(rx) then (exec1, exec2) -> s2; else (exec2, exec1) -> s2;          
            @s2 (dummy) -> s1;
          }"
      );
      Controller gezelController = model.Controllers["myctrl"];

			List<ExecutionSet> result = ControllerConversionHelper.getControlSteps(new SoftwareModel(model), gezelController);
      Assert.AreEqual(result.Count, 3); 

      ExecutionSet sfgExecutionSet;

      // check first sfg execution set
      sfgExecutionSet = result[0];
      Assert.AreEqual(sfgExecutionSet.Count, 2);
      Assert.AreEqual(sfgExecutionSet.ControlStepId, 0);
      Assert.AreEqual(sfgExecutionSet[0].LocalName, "exec1");
      Assert.AreEqual(sfgExecutionSet[1].LocalName, "exec2");
      // should be false since sfg parent datapath is controlled by fsm
      Assert.AreEqual(sfgExecutionSet[0].IsHardwired, false);
      Assert.AreEqual(sfgExecutionSet[1].IsHardwired, false);

      // check second sfg execution set
      sfgExecutionSet = result[1];
      Assert.AreEqual(sfgExecutionSet.Count, 2);
      Assert.AreEqual(sfgExecutionSet.ControlStepId, 1);
      Assert.AreEqual(sfgExecutionSet[0].LocalName, "exec2");
      Assert.AreEqual(sfgExecutionSet[1].LocalName, "exec1");
      // should be false since sfg parent datapath is controlled by fsm
      Assert.AreEqual(sfgExecutionSet[0].IsHardwired, false);
      Assert.AreEqual(sfgExecutionSet[1].IsHardwired, false);

      // check third sfg execution set
      sfgExecutionSet = result[2];
      Assert.AreEqual(sfgExecutionSet.Count, 1);
      Assert.AreEqual(sfgExecutionSet.ControlStepId, 2);
      Assert.AreEqual(sfgExecutionSet[0].LocalName, "dummy");
      // should be false since sfg parent datapath is controlled by fsm
      Assert.AreEqual(sfgExecutionSet[0].IsHardwired, false);
    }

    /// <summary>
    /// Tests the list of signal flow graph execution
    /// sets returned for a datapath controller by
    /// a complex, conditional fsm controller
    /// </summary>
    [TestMethod]
    public void TestComplexConditionalFsmSfgExecutionSet()
    {
      GezelModel model = GezelModelHelper.BuildModel(
        @"dp euclid {
            reg m, n               : ns(16);
            reg done               : ns(1);
            reg factor             : ns(16);

            sfg init    { }
            sfg flags   { }
            sfg reduce  { }
            sfg outidle { }
            sfg complete{ }
          }

        fsm myctrl(euclid) {
              initial s0;
              state s1, s2;

              @s0 (init, outidle) -> s1;
              @s1 if (done)               then (complete)                 -> s2;
                  else if ( m[0] &  n[0]) then (reduce, outidle, flags)   -> s1;   
                  else (outidle)                                          -> s1;       
        }"
      );
      Controller gezelController = model.Controllers["myctrl"];

			List<ExecutionSet> result = ControllerConversionHelper.getControlSteps(new SoftwareModel(model), gezelController);
      Assert.AreEqual(result.Count, 4);

      ExecutionSet sfgExecutionSet;

      // check first sfg execution set
      sfgExecutionSet = result[0];
      Assert.AreEqual(sfgExecutionSet.Count, 2);
      Assert.AreEqual(sfgExecutionSet.ControlStepId, 0);
      Assert.AreEqual(sfgExecutionSet[0].LocalName, "init");
      Assert.AreEqual(sfgExecutionSet[1].LocalName, "outidle");
      // should be false since sfg parent datapath is controlled by fsm
      Assert.AreEqual(sfgExecutionSet[0].IsHardwired, false);
      Assert.AreEqual(sfgExecutionSet[1].IsHardwired, false);

      // check second sfg execution set
      sfgExecutionSet = result[1];
      Assert.AreEqual(sfgExecutionSet.Count, 1);
      Assert.AreEqual(sfgExecutionSet.ControlStepId, 1);
      Assert.AreEqual(sfgExecutionSet[0].LocalName, "complete");
      // should be false since sfg parent datapath is controlled by fsm
      Assert.AreEqual(sfgExecutionSet[0].IsHardwired, false);

      // check third sfg execution set
      sfgExecutionSet = result[2];
      Assert.AreEqual(sfgExecutionSet.Count, 3);
      Assert.AreEqual(sfgExecutionSet.ControlStepId, 2);
      Assert.AreEqual(sfgExecutionSet[0].LocalName, "reduce");
      Assert.AreEqual(sfgExecutionSet[1].LocalName, "outidle");
      Assert.AreEqual(sfgExecutionSet[2].LocalName, "flags");
      // should be false since sfg parent datapath is controlled by fsm
      Assert.AreEqual(sfgExecutionSet[0].IsHardwired, false);
      Assert.AreEqual(sfgExecutionSet[1].IsHardwired, false);
      Assert.AreEqual(sfgExecutionSet[2].IsHardwired, false);

      // check forth sfg execution set
      sfgExecutionSet = result[3];
      Assert.AreEqual(sfgExecutionSet.Count, 1);
      Assert.AreEqual(sfgExecutionSet.ControlStepId, 3);
      Assert.AreEqual(sfgExecutionSet[0].LocalName, "outidle");      
      // should be false since sfg parent datapath is controlled by fsm
      Assert.AreEqual(sfgExecutionSet[0].IsHardwired, false);
    }
  }
}
